//
//  GYCenterManager.m
//  LockManagement
//
//  Created by apple on 2018/7/30.
//  Copyright © 2018年 yunjiazhineng. All rights reserved.
//

#import "GYCenterManager.h"
#import "GYPeriphreal.h"
#import "GYCharacteristic.h"
#import "GYService.h"
#import <UIKit/UIKit.h>

@interface GYCenterManager()<CBCentralManagerDelegate>

/** 当前扫描时间 */
@property (nonatomic, assign) NSTimeInterval scanTimeIntervel;

/** 当前蓝牙状态 */
@property (nonatomic, assign) CBManagerState managerState;

/** 蓝牙扫描到设备之后的block回调 */
@property (nonatomic, copy) blueManagerSearchDeviceBlock blueManagerSearchDeviceCallBack;

/** 蓝牙扫描条件(服务) */
@property (nonatomic, strong) NSArray *scanServiceArray;

/** 蓝牙扫描条件 */
@property (nonatomic, strong) NSDictionary *scanOptionsDic;

/** 发现设备dic */
@property (nonatomic, strong, readwrite) NSMutableDictionary *foundDeviceDic;

/** 链接设备dic */
@property (nonatomic, strong, readwrite) NSMutableDictionary *connectedDeviceDic;

/** 中心管理者 */
@property (nonatomic, strong, readwrite) CBCentralManager *centerMnaager;
@end

@implementation GYCenterManager

- (instancetype)initWithQueue:(dispatch_queue_t)queue {
    return [self initWithQueue:queue options:nil];
}

- (instancetype)initWithQueue:(dispatch_queue_t)queue options:(NSDictionary *)options {
    if (self = [super init]) {
        NSLog(@"manager   创建  ====%@",queue);
        self.centerMnaager = [[CBCentralManager alloc] initWithDelegate:self queue:queue options:options];
        self.scanTimeIntervel = LONG_MAX;
    }
    
    return self;
}


#pragma mark --  CBCentralManagerDelegate
//蓝牙状态发生改变的时候会 调用此方法
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    
    if (central.state != self.managerState) {
        //代表蓝牙的状态发生改变
        if (self.blueManagerStateChangedCallBack) {
            self.blueManagerStateChangedCallBack(self, central.state);
        }
        
        NSLog(@"系统蓝牙发生改变：之前状态：%zd ，改变后状态：%zd",self.managerState,central.state) ;
        self.managerState = central.state;
        
        //弹出警告框
        if (self.managerState == CBManagerStateUnsupported) {
            UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:@"此设备不支持BLE4.0,请更换设备" preferredStyle:UIAlertControllerStyleAlert];
            [UIApplication sharedApplication].keyWindow.rootViewController = alert ;
        }
        
        switch (central.state) {
            case CBManagerStatePoweredOn:
            {
                NSLog(@"蓝牙处于开始开启状态...");
            }
                break;
                
            case CBManagerStatePoweredOff:
            {
                NSLog(@"蓝牙处于关闭状态....");
            }
            default:
                break;
        }
    }
}

- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *,id> *)dict {
    
    NSLog(@"蓝牙状态即将重置........=====%@   dict=======%@",central,dict);
    //dict中会传入如下键值对
    /*
     3 //恢复连接的外设数组
     4 NSString *const CBCentralManagerRestoredStatePeripheralsKey;
     5 //恢复连接的服务UUID数组
     6 NSString *const CBCentralManagerRestoredStateScanServicesKey;
     7 //恢复连接的外设扫描属性字典数组
     8 NSString *const CBCentralManagerRestoredStateScanOptionsKey;
     9 */
}

//发现外围设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
    NSLog(@"found a device  name=====%@  identifier=======%@  RSSI=====%@",peripheral.name,peripheral.identifier,RSSI);
    
    //1.去掉重复搜索到的设备 (记录设备被发现的次数)
    NSInteger existedIndex = -1;
    for (NSString *temIdenfy in self.foundDeviceDic.allKeys) {
        if ([temIdenfy isEqualToString:peripheral.identifier.UUIDString]) {
            GYPeriphreal *tempPeriphreal = [self.foundDeviceDic objectForKey:temIdenfy];
            tempPeriphreal.deviceScanCount++;  //改变设备被发现次数啊
            existedIndex = tempPeriphreal.deviceScanCount;
            break;
        }
    }
    
    //代表发现新设备
    if (existedIndex == -1) {
        GYPeriphreal *per = [[GYPeriphreal alloc] initWithPeriphreal:peripheral central:self];
        per.RSSI = RSSI;
        per.advertisementData = advertisementData;
        //添加到发现设别字典中
        [self.foundDeviceDic setObject:per forKey:per.identifierString];
        
        //通知外部 搜索到新设备
        if (self.blueManagerSearchDeviceCallBack) {
            self.blueManagerSearchDeviceCallBack(per, searchFlagTypeAdded);
        }
    }else if (existedIndex%10 == 0) {//扫描此次设备超过10次
        //代表扫描到的是重复设备
        GYPeriphreal *tempP = self.foundDeviceDic[peripheral.identifier.UUIDString];
        tempP.RSSI = RSSI ;
        tempP.deviceScanCount = 0 ;
        tempP.advertisementData = advertisementData ;
        if (self.blueManagerSearchDeviceCallBack) {
            self.blueManagerSearchDeviceCallBack(tempP , searchFlagTypeChanged );
        }
    }
}

#pragma mark -- conntect device

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    NSLog(@"connect device success  per========%@  identifier======%@",peripheral,peripheral.identifier);
    
    //如果这个设备在连接的列表中,那么直接找到这个链接上的设备
    GYPeriphreal *tempPer = nil;
    for (NSString *tempIden in self.connectedDeviceDic.allKeys) {
        if ([tempIden isEqualToString:peripheral.identifier.UUIDString]) {
            tempPer = self.connectedDeviceDic[tempIden];
            break;
        }
    }
    
    //如果这个设备没有连接过(链接的缓存列表中不存在这个设备), 则在发现设备的列表中寻找
    if (!tempPer) {
        for (NSString *tempIden in self.foundDeviceDic.allKeys) {
            if ([tempIden isEqualToString:peripheral.identifier.UUIDString]) {
                tempPer = self.foundDeviceDic[tempIden];
                break;
            }
        }
        
        //在发现设别的列表中发现有这个设备之后
        if (tempPer) {
            //把这个链接的设备添加到
            [self.connectedDeviceDic setObject:tempPer forKey:peripheral.identifier.UUIDString];
        }else {
            tempPer = [[GYPeriphreal alloc] initWithPeriphreal:peripheral central:self];
            [self.connectedDeviceDic setObject:tempPer forKey:peripheral.identifier.UUIDString];
            [self.foundDeviceDic setObject:tempPer forKey:peripheral.identifier.UUIDString];
        }
    }
    
    //链接上设备之后开始扫描服务
    [tempPer dealDeviceConnectWithError:nil deviceConnectType:deviceConntTypeSuccess];
}

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    NSLog(@"device connect Failed  uuid=======%@   error========%@",peripheral.identifier,error);
    //在连接的设备列表中寻找有没有这个设备
    GYPeriphreal *errorPer = nil ;
    for (NSString *tempP in [self.connectedDeviceDic allKeys]) {
        if ([tempP isEqualToString:peripheral.identifier.UUIDString]) {
            errorPer = self.connectedDeviceDic[tempP];
            break  ;
        }
    }
    
    //如果有则删除这个设备
    if (errorPer) {
        [self.connectedDeviceDic removeObjectForKey:errorPer.identifierString];
        errorPer = nil ;
    }else{
        for (NSString *tempIden in [self.foundDeviceDic allKeys]) {
            if ([tempIden isEqualToString:peripheral.identifier.UUIDString]) {
                errorPer = self.foundDeviceDic[tempIden] ;
                break  ;
            }
        }
        NSLog(@"attention: you should deal with this error");
    }
    
    NSAssert(errorPer, @"attention: you should deal with this error");
    //设备连接失败之后, 你们应该要通知外部做一些处理
    if (self.blueManagerSearchDeviceCallBack) {
        self.blueManagerSearchDeviceCallBack(errorPer, searchFlagTypeDisconnect);
    }
    
    //外部连接的结果
    if (errorPer) {
        [errorPer dealDeviceConnectWithError:error deviceConnectType:deviceConntTypeFailed];
    }
}

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    NSLog(@"device disConnect  uuid=========%@   error========%@",peripheral.identifier,error);
    
    GYPeriphreal *disPer = nil ;
    for (NSString *tempIden in [self.connectedDeviceDic allKeys]) {
        if ([tempIden isEqualToString:peripheral.identifier.UUIDString]) {
            disPer = self.connectedDeviceDic[tempIden] ;
            break  ;
        }
    }
    
    //断开设备之后----- 你应该清空这个设备下的所有服务  包括设备的状态
    if (disPer) {
        for (GYService *tempS in disPer.serviceArray) {
            tempS.service = nil;
            [tempS.characteristicArray removeAllObjects];
            tempS.isOn = NO;
            tempS.isEnabled = NO;
        }
        [disPer.serviceArray removeAllObjects];
        
        //移除列表中的设备
        [self.connectedDeviceDic removeObjectForKey:disPer.identifierString];
        [self.foundDeviceDic removeObjectForKey:disPer.identifierString];
    }else{
        NSAssert(NO, @"attention: you should deal with this error");
    }
    
    //外部变动 我们需要通知外围涉笔
    if (self.blueManagerSearchDeviceCallBack && disPer) {
        self.blueManagerSearchDeviceCallBack(disPer, searchFlagTypeDisconnect);
    }
    
    if (error && disPer) {
        [disPer dealDeviceConnectWithError:error deviceConnectType:deviceConntTypeDisConnect];
    }
    
    if (disPer) {
        disPer = nil;
    }
    
}



#pragma mark -- 扫描

- (void)startScanDevice {
    [self scanDeviceWithTimeCallback:self.blueManagerSearchDeviceCallBack];
}

- (void)scanDeviceWithTimeCallback:(blueManagerSearchDeviceBlock)searchDeviceCallBack {
    [self scanDeviceWithTimeInterval:self.scanTimeIntervel callBack:self.blueManagerSearchDeviceCallBack];
}

- (void)scanDeviceWithTimeInterval:(NSTimeInterval)timeInterval callBack:(blueManagerSearchDeviceBlock)searchDeviceCallBack {
    [self scanDeviceWithTimeInterval:self.scanTimeIntervel services:self.scanServiceArray options:self.scanOptionsDic callBack:self.blueManagerSearchDeviceCallBack];
}

- (void)scanDeviceWithTimeInterval:(NSTimeInterval)timeInterval services:(NSArray *)service options:(NSDictionary *)options callBack:(blueManagerSearchDeviceBlock)searchDeviceCallBack {
    NSAssert(searchDeviceCallBack, @"search device is nil");//断言  这个block为nil的时候 会执行这个断言
    //赋值
    self.scanTimeIntervel = timeInterval;
    self.scanServiceArray = service;
    self.scanOptionsDic = options;
    
    if (searchDeviceCallBack) {
        self.blueManagerSearchDeviceCallBack = searchDeviceCallBack;
    }
    
    self.scanning = YES;//代表正在扫描
    
    //根据传入的服务id数组 获取所有的链接设备 (检验传入中service的设备有没有在已经存在数组中,没有则添加)
    NSArray *connectedArray = [self retrieveConnectedPeripheralsWithServices:service];
    [connectedArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        GYPeriphreal *per = (GYPeriphreal *)obj;
        BOOL isExtend = NO;
        
        //循环遍历 链接的设备中 有没有这个设备
        for (NSString *tempIdentify in [self.foundDeviceDic allKeys]) {
            if ([tempIdentify isEqualToString:per.identifierString]) {
                isExtend = YES ;
                break  ;
            }
        }
        
        if (!isExtend) {
            [self.foundDeviceDic setObject:per forKey:per.identifierString];
        }
        
        //证明
        if (self.blueManagerSearchDeviceCallBack) {
            self.blueManagerSearchDeviceCallBack(per, isExtend ? searchFlagTypeChanged : searchFlagTypeAdded);
        }
    }];
    
    NSLog(@"开始扫描设备------倒计时=======%0.f时长",timeInterval);
    [self.centerMnaager scanForPeripheralsWithServices:service options:options];
    
    //到达制定时间之后 要通知外部
    __weak typeof(self) weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeInterval * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        weakSelf.scanning = NO;
        
        if (weakSelf.centerMnaager.isScanning && weakSelf.blueManagerSearchDeviceCallBack) {
            //停止扫描
            [weakSelf stopScanDevice];
            weakSelf.blueManagerSearchDeviceCallBack(nil, searchFlagTypeFinish);
        }
        
        //取消执行某个方法
        for (GYPeriphreal *per in self.foundDeviceDic.allValues) {
            
            //取消执行外围设备被发现超时的执行方法
            [NSObject cancelPreviousPerformRequestsWithTarget:per selector:@selector(foundDeviceTimeout:) object:nil];
        }
    });
    
}

- (void)stopScanDevice {
    if (self.scanning) {
        self.scanning = NO;
    }
    
    NSLog(@"停止扫描设备...");
    [self.centerMnaager stopScan];
}

- (void)removeAllScanFoundDevice {
    [self.foundDeviceDic removeAllObjects];
}

- (void)disConnectAllDevice {
    for (GYPeriphreal *per in self.connectedDeviceDic.allValues) {
        [per disConnectDevice];
    }
}

- (GYPeriphreal *)searchDeviceWithPeripheral:(CBPeripheral *)peripheral {
    GYPeriphreal *result = nil;
    NSArray *tempArray = [NSArray arrayWithArray:[self.connectedDeviceDic allValues]];
    for (GYPeriphreal *tempPeripheral in tempArray) {
        if ([tempPeripheral.periphreal isEqual: peripheral]) {
            result = tempPeripheral;
            break;
        }
    }
    return result;
}

//超时连接操作
- (void)foundDeviceTimeout:(GYPeriphreal *)perpheral {
    NSArray *allValues = [self.connectedDeviceDic allValues];
    [allValues enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj isEqual:perpheral]) {
            *stop = YES ;
            return  ;
        }
    }];
    
    //如果连接超时之后 要在发现设备列表中  删除这个设备(扫秒超时 要清楚缓存)
    __block BOOL isExitDevice ;
    NSArray *tempAllValues = self.foundDeviceDic.allValues;
    [tempAllValues enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([obj isEqual:perpheral]) {
            isExitDevice = YES ;
            *stop = YES ;
        }
    }];
    if (isExitDevice) {
        [self.foundDeviceDic removeObjectForKey:perpheral.identifierString];
        
        if (self.blueManagerSearchDeviceCallBack) {
            self.blueManagerSearchDeviceCallBack(perpheral , searchFlagTypeDelete );
        }
    }
}

//根据服务id获取蓝牙链接的设备
- (NSArray *)retrieveConnectedPeripheralsWithServices:(NSArray *)serviceUUIDS {
    
    NSLog(@"根据服务serviceIDs 来获取中心设备连接上的外围设备======%@",serviceUUIDS);
    if (serviceUUIDS.count == 0) {
        return @[];
    }
    
    //更具服务ID返回中心设备链接的所有外围设备,(包括其他程序链接的设备)
    NSArray *resultConnectedDevices = [self.centerMnaager retrieveConnectedPeripheralsWithServices:serviceUUIDS];
    
    NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:resultConnectedDevices.count];
    for (CBPeripheral *periphreal in resultConnectedDevices) {
        //把链接的所有外围设备转换成
        GYPeriphreal *temPeriphreal = [[GYPeriphreal alloc] initWithPeriphreal:periphreal central:self];
        [tempArray addObject:temPeriphreal];
    }
    
    return tempArray;
}

- (NSArray *)retrievePeripheralsWithIdentifiers:(NSArray *)identifiers {
    
    NSLog(@"蓝牙获取系统已知设备========%@",identifiers);//设备的uuid
    NSArray *resultArray = [self.centerMnaager retrievePeripheralsWithIdentifiers:identifiers];
    
    NSMutableArray *tempArray = [NSMutableArray arrayWithCapacity:resultArray.count];
    for (CBPeripheral *periphreal in resultArray) {
        //把链接的所有外围设备转换成
        GYPeriphreal *temPeriphreal = [[GYPeriphreal alloc] initWithPeriphreal:periphreal central:self];
        [tempArray addObject:temPeriphreal];
    }
    
    return tempArray;
}


#pragma mark -- 懒加载

- (NSMutableDictionary *)foundDeviceDic {
    if (!_foundDeviceDic) {
        _foundDeviceDic = [NSMutableDictionary dictionary];
    }
    return _foundDeviceDic;
}

- (NSMutableDictionary *)connectedDeviceDic {
    if (!_connectedDeviceDic) {
        _connectedDeviceDic = [NSMutableDictionary dictionary];
    }
    
    return _connectedDeviceDic;
}
















@end
